#include "stdafx.h"
#include "SpriteSheet.h"
#include "SDLTexture.h"
#include <iostream>
#include <fstream>

#include "SpriteSheetAnimation.h"
#include "XMLParsers.h"

using namespace std;

namespace ZeEngine
{

    SpriteSheet::SpriteSheet(const string& strPath, 
                            const string& strName, 
                            const string& strGroup) : Resource(strPath, strName, strGroup), m_pTexture(NULL)
    {
    }

    SpriteSheet::~SpriteSheet()
    {
    }

    SDL_Rect SpriteSheet::GetSpriteBounds(const std::string& strName)
    {
        SDL_Rect rect = {0, 0, 0, 0};

        auto it = m_sprites.find(strName);

        if (it != m_sprites.end())
        {
            rect = it->second;
        }
        else
        {
            LOG_ERROR("Could not find sprite: " + strName + " in sprite sheet: " + GetName() + " with file: " + GetPath() + " in group: " + GetGroup());
        }

        return rect;
    }

    bool SpriteSheet::Load(ResourceLoader& loader)
    {
        bool fSuccess = true;

        ifstream file(GetPath());
        string line;

        if (file.is_open())
        {
            string line;

            while(getline(file,line))
            {
                //Tokenize it
                vector<string> tokens;
                istringstream iss(line);
                copy(istream_iterator<string>(iss),
                istream_iterator<string>(),
                back_inserter<vector<string> >(tokens));

                if (tokens.size() == 6)
                {
                    const string name = tokens[0];
                    const s32 x = Utilities::ConvertStringToType<s32>(tokens[2]);
                    const s32 y = Utilities::ConvertStringToType<s32>(tokens[3]);
                    const s32 width = Utilities::ConvertStringToType<s32>(tokens[4]);
                    const s32 height = Utilities::ConvertStringToType<s32>(tokens[5]);

                    SDL_Rect rect = {x, y, width, height};
                    m_sprites.insert(SpriteSheetMapPair(name, rect));
                }
                else
                {
                    fSuccess = false;
                    LOG_ERROR("Invalid sprite sheet: " + GetName() + " with file: " + GetPath() + " in group: " + GetGroup());
                }
            }

            file.close();       
        }
        else
        {
            fSuccess = false;
            LOG_ERROR("Could not open SpriteSheet: " + GetName() + " with file: " + GetPath() + " in group: " + GetGroup());
        }

        if (fSuccess)
        {
            fSuccess = LoadAnimations();
        }

        SetLoaded(fSuccess);

        return fSuccess;
    }
    
    bool SpriteSheet::LoadAnimations()
    {
        bool fSuccess = true;

        for (auto it = m_animationsToLoad.begin(); it != m_animationsToLoad.end(); ++it)
        {
            string strAnimationFile = *it;

            XMLParsers parser(strAnimationFile);

            if (parser.IsLoaded())
            {
                XMLNodeList animations = parser.GetNodeList("SpriteSheetAnimations.SpriteSheetAnimation");

                for (auto it = animations.begin(); it != animations.end(); ++it)
                {
                    const XMLNode* pAnimationsNode = (*it);

                    if (pAnimationsNode)
                    {
                        const string animationName = pAnimationsNode->GetAttribute<string>("name");

                        if (animationName.length() > 0)
                        {
                            bool reverse = false;
                            vector<SDL_Rect> frames;
                            vector<s32>      frameTime;
                            for (auto jt = pAnimationsNode->GetChildren().begin(); jt != pAnimationsNode->GetChildren().end(); ++ jt)
                            {
                                const XMLNode* pAnimationNode = (*jt).second;

                                if (pAnimationNode)
                                {
                                    const string type = pAnimationNode->GetName();
                                    string strValue = pAnimationNode->GetValueString();
                                    Utilities::TrimString(strValue);
                                    if (type == "Reverse")
                                    {
                                        transform(strValue.begin(), strValue.end(), strValue.begin(), tolower);
                                        reverse = strValue == "true" ? true : false;
                                    }
                                    else if (type == "FrameOrder")
                                    {
                                        istringstream ss(strValue);
                                        string token;

                                        while(getline(ss, token, ',')) 
                                        {
                                            Utilities::TrimString(token);
                                            frames.push_back(GetSpriteBounds(token));
                                        }
                                    }
                                    else if (type == "FrameDuration")
                                    {
                                        istringstream ss(strValue);
                                        string token;

                                        while(getline(ss, token, ',')) 
                                        {
                                            Utilities::TrimString(token);
                                            s32 frameDuration = Utilities::ConvertStringToType<u32>(token);
                                            frameTime.push_back(frameDuration);
                                        }
                                    }
                                }
                            }

                            if (frameTime.size() == frames.size() && frameTime.size() > 0)
                            {
                                m_animations.insert(SpriteSheetAnimationsMapPair(animationName, new SpriteSheetAnimation(animationName, reverse, frames, frameTime)));
                            }
                            else
                            {
                                LOG_ERROR("Error with animation " + animationName + "in file " + strAnimationFile + " either has no aniations or the frame duration does not match the frames");
                            }
                        }
                        else
                        {
                            fSuccess = false;
                            LOG_ERROR("Could not find animation name for sprite sheet animation file " + strAnimationFile);
                        }
                    }
                }   
            }
            else
            {
                fSuccess = false;
                LOG_ERROR("Error opening animation file: " + strAnimationFile);
            }
        }

        return fSuccess;
    }

    const SpriteSheetAnimation* SpriteSheet::GetAnimation(const string& strAnimationName) const
    {
        SpriteSheetAnimation* pAnimation = NULL;

        auto it = m_animations.find(strAnimationName);

        if (it != m_animations.end())
        {
            pAnimation = it->second;
        }
        else
        { 
            LOG_WARNING("Animation " + strAnimationName  + "not found in sprite sheet " + GetName());
        }
        return pAnimation;
    }


    void SpriteSheet::AddAnimations(const std::string& strAnimationName, const std::string& strAnimationFile)
    {
        m_animationsToLoad.push_back(strAnimationFile);
    }

    void SpriteSheet::Unload(ResourceLoader& loader)
    {
        SetLoaded(false);
        m_sprites.clear();
        m_pTexture = NULL;
    }

    void SpriteSheet::SetTexture(SDLTexture* pTexture)
    {
        m_pTexture = pTexture;
    }
}
